<?php
/**
 * Http 相关处理
 * */
namespace socket;

class Http
{
    protected $socket;

    protected $route = [];

    protected $maxBytes;

    protected $readBufferSize = 2048;

    public function __construct($addr, $port, $maxBytes = 8388608)
    {
        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if (!$this->socket){
            exit("Create Socket Failed.");
        }
        socket_bind($this->socket, $addr, $port);

        $this->route = [
            'POST' => [],
            'GET' => [],
            'PUT' => [],
            'HEAD' => [],
            'DELETE' => [],
        ];
        $this->maxBytes = $maxBytes;
    }

    /**
     * 设置读取的 buffer 大小
     * @param   integer $size
     * @return  Http
     * */
    public function setReadBufferSize($size) {
        $this->readBufferSize = $size;
        return $this;
    }

    /**
     * 运行
     * */
    public function run() {
        socket_listen($this->socket, 5);
        while (true) {
            try {
                $conn = socket_accept($this->socket);
                $msg = '';
                $byteCount = 0;
                // TODO: 请求信息读取何时结束还需要继续了解
                while (($tmp = socket_read($conn, $this->readBufferSize, PHP_BINARY_READ)) !== false) {
                    if ($byteCount >= $this->maxBytes){
                        HttpResponse::entityTooLarge($conn);
                    }
                    $msg .= $tmp;
                    if (substr($tmp, strlen($tmp) - 4, 4) === "\r\n\r\n"){  // end
                        $this->process($conn, $msg);
                        socket_close($conn);
                        break;
                    }
                    $byteCount += $this->readBufferSize;
                }
            }catch (\Exception $e) {
                print_r($e->getMessage());
            }
        }
    }

    /**
     * 处理请求信息
     * @param   resource    $conn
     * @param   string      $requestBody
     * */
    protected function process($conn, $requestBody) {
        $returnData = $this->parseRequest($requestBody);
        if (!$returnData[1]){
            HttpResponse::notFound($conn);
        }else{
            $func = $returnData[1]->callAble;
            $func($conn, $returnData[0], HttpResponse::class);
        }
    }

    /**
     * 解析请求
     * @param   string  $requestBody
     * @return  array
     * */
    protected function parseRequest($requestBody) {
        $request = new HttpRequest($requestBody);
        $request->parseInfo();
        $callBack = false;
        switch ($request->requestMethod){
            case HttpMethod::GET:
                $callBack = isset($this->route[HttpMethod::GET][$request->requestUrl]) ? $this->route[HttpMethod::GET][$request->requestUrl] : false;
                break;
            case HttpMethod::POST:
                $callBack = isset($this->route[HttpMethod::POST][$request->requestUrl]) ? $this->route[HttpMethod::POST][$request->requestUrl] : false;
                break;
            case HttpMethod::HEAD:
                $callBack = isset($this->route[HttpMethod::HEAD][$request->requestUrl]) ? $this->route[HttpMethod::HEAD][$request->requestUrl] : false;
                break;
            case HttpMethod::PUT:
                $callBack = isset($this->route[HttpMethod::PUT][$request->requestUrl]) ? $this->route[HttpMethod::PUT][$request->requestUrl] : false;
                break;
            case HttpMethod::DELETE:
                $callBack = isset($this->route[HttpMethod::DELETE][$request->requestUrl]) ? $this->route[HttpMethod::DELETE][$request->requestUrl] : false;
                break;
        }
        return [$request, $callBack];
    }

    /**
     * GET 方式的路由
     * @param   string      $url
     * @param   callable    $callAble
     * @return  Http
     * */
    public function get($url, $callAble) {
        $this->route['GET'][$url] = new Route($url, HttpMethod::GET, $callAble);
        return $this;
    }

    /**
     * POST 方式的路由
     * @param   string      $url
     * @param   callable    $callAble
     * @return  Http
     * */
    public function post($url, $callAble) {
        $this->route['POST'][$url] = new Route($url, HttpMethod::POST, $callAble);
        return $this;
    }

    /**
     * PUT 方式的路由
     * @param   string      $url
     * @param   callable    $callAble
     * @return  Http
     * */
    public function put($url, $callAble) {
        $this->route['PUT'][$url] = new Route($url, HttpMethod::PUT, $callAble);
        return  $this;
    }

    /**
     * DELETE 方式的路由
     * @param   string      $url
     * @param   callable    $callAble
     * @return  Http
     * */
    public function delete($url, $callAble) {
        $this->route['DELETE'][$url] = new Route($url, HttpMethod::DELETE, $callAble);
        return $this;
    }

    /**
     * HEAD 方式的路由
     * @param   string      $url
     * @param   callable    $callAble
     * @return  Http
     * */
    public function head($url, $callAble) {
        $this->route['HEAD'][$url] = new Route($url, HttpMethod::HEAD, $callAble);
        return $this;
    }
}
